<script setup>
const props = defineProps({
  id: String,
  data: Array,
  divideCount: Number
})

const rectColorMap = new Map()
rectColorMap.set(1, '#EFF7FF')
rectColorMap.set(2, '#FFE9E9')
rectColorMap.set(3, '#b3e19d')

const borderColorMap = new Map()
borderColorMap.set(1, '#3E8FF7')
borderColorMap.set(2, '#FB602D')
borderColorMap.set(3, '#67C23A')

const info = reactive({
  width: 800,
  height: 320,
  divideCount: props.divideCount || 10,
  ctx: null,
  bkDirection: 'right',
  points: [],
  rects: [],
  originSource: [],
  triangles: {},
  dynamicRects: {},
  dynamicTriangles: {},
  rectTexts: {},
  titleTexts: {},
  tagTexts: {},
  tags: {},
  arcRadius: 0,
  triangleColor: '#62a4f9',
  data: [],
  raf: null,
  steps: 30,
  pathEndY: 0,
  pathStartY: 0,
  arcLocationY: 0,
  backgroundPoints: [],
  index: 0,
  circleCount: 1
})
// 格子的直径
const radius = info.width / info.divideCount / 1.5
// 格子的半径
const halfRadius = radius / 2

info.height = radius * 4 + 80

const drawArc = (options) => {
  let endPath

  if (options.direct === 'right') {
    endPath = -0.5 + options.step
  }
  if (options.direct === 'left') {
    endPath = options.beginPath + options.step
  }

  var lastX =
    options.startX + info.arcRadius * Math.cos((endPath - 0.01) * Math.PI)
  var lastY =
    options.startY + info.arcRadius * Math.sin((endPath - 0.01) * Math.PI)
  let curX = options.startX + info.arcRadius * Math.cos(endPath * Math.PI)
  let curY = options.startY + info.arcRadius * Math.sin(endPath * Math.PI)

  const target = {
    startX: lastX,
    startY: lastY,
    endX: curX,
    endY: curY
  }
  return target
}

const clear = () => {
  info.ctx?.clearRect(0, 0, info.width, info.height)
}

const drawArrow2 = (x, y, color, direct) => {
  info.ctx.beginPath()
  let size = (1 / 8) * radius
  if (direct === 'down') {
    info.ctx.moveTo(x - size, y)
    info.ctx.lineTo(x, y + size)
    info.ctx.lineTo(x + size, y)
  }
  if (direct === 'up') {
    info.ctx.moveTo(x - size, y)
    info.ctx.lineTo(x, y - size)
    info.ctx.lineTo(x + size, y)
  }

  info.ctx.fillStyle = color
  info.ctx.closePath()
  info.ctx.fill()
}

function drawArrow(x, y) {
  info.ctx.beginPath()
  info.ctx.arc(x, y, 8, 0, 2 * Math.PI)

  info.ctx.fillStyle = borderColorMap.get(info.circleCount) || '#4b90ff'
  info.ctx.closePath()
  info.ctx.fill()
}

const drawText = (item) => {
  info.ctx.font = `${item.fontSize || 16}px Arial`
  info.ctx.fillStyle = item.fillStyle || '#000'
  info.ctx.fillText(item.text, item.startX, item.startY + radius / 2)
}

const drawRect = (item, radius = 8) => {
  if (!item) return
  info.ctx.fillStyle = item.fillStyle
  info.ctx.strokeStyle = item.strokeStyle
  info.ctx.beginPath()
  info.ctx.moveTo(item.startX + radius, item.startY)
  info.ctx.lineTo(item.startX + item.endX - radius, item.startY)
  info.ctx.quadraticCurveTo(
    item.startX + item.endX,
    item.startY,
    item.startX + item.endX,
    item.startY + radius
  )
  info.ctx.lineTo(item.startX + item.endX, item.startY + item.endY - radius)
  info.ctx.quadraticCurveTo(
    item.startX + item.endX,
    item.startY + item.endY,
    item.startX + item.endX - radius,
    item.startY + item.endY
  )
  info.ctx.lineTo(item.startX + radius, item.startY + item.endY)
  info.ctx.quadraticCurveTo(
    item.startX,
    item.startY + item.endY,
    item.startX,
    item.startY + item.endY - radius
  )
  info.ctx.lineTo(item.startX, item.startY + radius)
  info.ctx.quadraticCurveTo(
    item.startX,
    item.startY,
    item.startX + radius,
    item.startY
  )
  info.ctx.closePath()

  // info.ctx.shadowColor = '#e0e0e0'
  // info.ctx.shadowOffsetX = 2
  // info.ctx.shadowOffsetY = 2
  // info.ctx.shadowBlur = 8

  info.ctx.fill()
  info.ctx.strokeStyle = item.strokeStyle // 这里的 'blue' 可以替换成任何你想要的颜色
  info.ctx.lineWidth = 1 // 这里的 2 可以替换成任何你想要的边框宽度
  info.ctx.stroke()

  // info.ctx.shadowOffsetX = 0
  // info.ctx.shadowOffsetY = 0
  // info.ctx.shadowBlur = 0
}

// 背景线
const drawLine = (ctx, points, options) => {
  points.forEach((item) => {
    if (!item) return
    ctx.beginPath()
    ctx.lineWidth = options?.lineWidth || 5
    ctx.lineCap = 'round'
    ctx.moveTo(item.startX - 0.5, item.startY)
    ctx.lineTo(item.endX + 0.5, item.endY)

    ctx.strokeStyle = options?.strokeStyle || '#4b90ff'
    ctx.stroke()
  })
}

const drawDynamicLine = (ctx, points, options) => {
  if (info.index < points.length) {
    info.index = info.index + 1
  }

  if (info.circleCount > info.maxCircle) {
    info.index = 0
    info.circleCount = 1

    for (let tIdx in info.rects) {
      info.rects[tIdx].fillStyle = '#fff'
      info.rects[tIdx].strokeStyle = '#fff'
      info.triangles[tIdx].strokeStyle = '#fff'
      info.triangles[tIdx].fillStyle = '#fff'
      info.rectTexts[tIdx].fillStyle = 'transparent'
    }
  }
  // 一圈重置
  if (points && points.length && info.index >= points.length) {
    info.index = 0
    info.circleCount++
  }

  info.originSource = points.slice(0, info.index)
  // 大圆点
  const tPoint = points[info.index]
  tPoint && drawArrow(tPoint.endX, tPoint.endY)

  // 动态格子 三角形
  if (tPoint && tPoint.changeStyle) {
    const targetData = props.data[tPoint.rectIdx]
    const changeFlag = targetData.circle.includes(info.circleCount)

    if (changeFlag) {
      const curRectColor = rectColorMap.get(info.circleCount)
      const curBorderColor = borderColorMap.get(info.circleCount)
      info.rects[tPoint.rectIdx].fillStyle = curRectColor
      info.rects[tPoint.rectIdx].strokeStyle = curBorderColor

      info.triangles[tPoint.rectIdx].strokeStyle = curBorderColor
      info.triangles[tPoint.rectIdx].fillStyle = curBorderColor

      info.rectTexts[tPoint.rectIdx].fillStyle = curBorderColor
    }
  }

  info.originSource.forEach((item) => {
    if (!item) return
    ctx.beginPath()
    ctx.lineWidth = options?.lineWidth || 5
    ctx.lineCap = 'round'
    ctx.moveTo(item.startX - 0.5, item.startY)
    ctx.lineTo(item.endX + 0.5, item.endY)

    ctx.strokeStyle = borderColorMap.get(info.circleCount)
    // ctx.strokeStyle = options?.strokeStyle || '#4b90ff'
    ctx.stroke()
  })
}

const animate = () => {
  clear()
  info.raf = requestAnimationFrame(animate)

  // 静态箭头
  for (let item in info.triangles) {
    item &&
      drawArrow2(
        info.triangles[item].x,
        info.triangles[item].y,
        info.triangles[item].fillStyle,
        info.triangles[item].direct
      )
  }
  //动态箭头
  for (let item in info.dynamicTriangles) {
    item &&
      drawArrow2(
        info.dynamicTriangles[item].x,
        info.dynamicTriangles[item].y,
        info.dynamicTriangles[item].fillStyle,
        info.dynamicTriangles[item].direct
      )
  }
  // 格子
  for (let item in info.rects) {
    item && drawRect(info.rects[item], 4)
  }
  // 背景线
  drawLine(info.ctx, info.backgroundPoints, {
    strokeStyle: '#fff',
    lineWidth: 8
  })
  // 工序文字处理
  for (let item in info.rectTexts) {
    item && drawText(info.rectTexts[item])
  }
  // 站位背景框处理
  for (let item in info.tags) {
    const target = info.tags[item]
    const strokeStyle = info.rects[item].strokeStyle
    target.fillStyle = ['#fff', '#ffffff'].includes(strokeStyle)
      ? '#3E8FF7'
      : strokeStyle
    target.strokeStyle = target.fillStyle
    item && drawRect(info.tags[item], 4)
  }
  // 站位文字处理
  for (let item in info.tagTexts) {
    item && drawText(info.tagTexts[item])
  }
  // 动态线
  if (info.backgroundPoints && info.backgroundPoints.length) {
    drawDynamicLine(info.ctx, info.backgroundPoints, {
      strokeStyle: '#4b90ff',
      lineWidth: 4
    })
  }
}

const getPartLen = (count) => {
  if (count > 99) return 3
  if (count > 9) return 2
  return 1
}

const drawInitBackgroundLine = () => {
  info.data.forEach((item, idx) => {
    const target = item.parts.slice(0, 2)
    console.log(
      '🚀 ~ file: canvasPath.vue:312 ~ info.data.forEach ~ target:',
      target
    )
    // 字号处理
    const fontFlag = target.some((i) => i >= 10)
    const textInfo = {
      startX: item.x + halfRadius,
      startY: item.y + 8,
      text: `${target.join(',')}`,
      fontSize: fontFlag ? 14 : 16,
      fillStyle: 'transparent'
    }
    // 文字长度
    const partsLen = item.parts.reduce((prev, item) => {
      return prev + getPartLen(item)
    }, 0)
    if (partsLen === 1) {
      textInfo.startX = item.x + halfRadius - 4
    }
    if (partsLen === 2 && item.parts.length === 2) {
      textInfo.startX = item.x + halfRadius - textInfo.fontSize + 4
    }
    if (partsLen === 2 && item.parts.length === 1) {
      textInfo.startX = item.x + halfRadius - textInfo.fontSize / 2
    }
    if (partsLen === 3) {
      textInfo.startX = item.x + halfRadius - textInfo.fontSize
    }
    if (partsLen === 4) {
      textInfo.startX = item.x + halfRadius - textInfo.fontSize - 4
    }

    info.rectTexts[idx] = textInfo

    let startX, endX, distanceX
    if (idx > 0) {
      startX = info.data[idx - 1].x + halfRadius
    }
    if (idx === 0) {
      startX = info.data[idx].x + halfRadius
    }

    endX = item.x + halfRadius
    for (let i = 0; i < info.steps; i++) {
      // 箭头运动方向
      if (idx < info.divideCount) {
        info.bkDirection = 'right'
      }
      if (idx % info.divideCount === 0 && idx % (2 * info.divideCount) !== 0) {
        info.bkDirection = 'down'
      }
      if (idx > info.divideCount) {
        info.bkDirection = 'left'
      }
      if (idx % (2 * info.divideCount) === 0 && idx !== 0) {
        info.bkDirection = 'up'
      }
      distanceX = (endX - startX) / info.steps || 0
      let point = {}
      if (info.bkDirection === 'right') {
        point = {
          startX: startX,
          startY: info.pathStartY,
          endX: startX + distanceX * (i + 1),
          endY: info.pathStartY
        }
      }
      if (info.bkDirection === 'left') {
        point = {
          startX: startX,
          startY: info.pathEndY,
          endX: startX + distanceX * (i + 1),
          endY: info.pathEndY
        }
      }
      // 获取圆心位置
      if (idx % info.divideCount === 0 && idx % (2 * info.divideCount) !== 0) {
        let t = {
          startX: startX - 4,
          startY: info.arcLocationY,
          radius: info.arcRadius,
          direct: 'right',
          beginPath: 1.5,
          step: i / info.steps
        }

        point = drawArc(t)
      }
      // 处理箭头路径的点
      if (point) {
        point.rectIdx = idx
        point.changeStyle = info.rectTexts?.[idx]?.text ? true : false
        info.backgroundPoints.push(point)
      }
    }
  })

  // 左侧半圆的点位
  let lastStartX = info.data.at(-1).x + halfRadius
  for (let i = 0; i < info.steps; i++) {
    info.bkDirection = 'up'
    let tp = {
      startX: lastStartX + 4,
      startY: info.arcLocationY,
      radius: info.arcRadius,
      direct: 'left',
      beginPath: 0.5,
      step: i / info.steps
    }
    const point = drawArc(tp)
    info.backgroundPoints.push(point)
  }
}

const init = () => {
  info.points = []
  info.rects = {}
  info.dynamicRects = {}
  info.triangles = {}
  info.dynamicTriangles = {}
  info.backgroundPoints = []
  info.originSource = []
  info.rectTexts = {}
  info.titleTexts = {}
  info.tagTexts = {}
  info.tags = {}
  info.finish = false
}

const initPathInfo = () => {
  if (!info.pathStartY && info.data && info.data.length) {
    info.pathStartY = info.data[0].y + 3 * info.rectWidth + 32
  }
  if (!info.pathEndY && info.data && info.data.length) {
    // 轨迹圆的间距
    info.pathEndY = info.pathStartY + 50
  }
  if (!info.arcRadius && info.pathEndY && info.pathStartY) {
    // 轨迹圆的半径
    info.arcRadius = 25

    info.arcLocationY = info.pathStartY + 25
  }
}

const drawInitRects = () => {
  const width = info.width / (info.data.length * 2) // 盒子宽度
  info.rectWidth = width
  const spacingX = 40 // 左边距
  const spacingY = 20 // 上边距
  const distanceW = 3.8

  const newData = info.data.map((item, idx) => {
    // 下面数据 rect的中心坐标
    let type
    if (idx + 1 > info.divideCount) {
      item.x = spacingX + width * (info.data.length - 1 - idx) * distanceW
      item.y = info.pathEndY + info.rectWidth + 16
      type = 'down'
    }
    // 上面数据
    if (idx < info.divideCount) {
      item.x = spacingX + info.rectWidth * distanceW * idx
      item.y = spacingY + info.rectWidth - 16
      type = 'up'

      if (idx === 0) {
        initPathInfo()
      }
      if (idx + 1 === info.divideCount) {
        info.maxDistance = item.x - info.data[0].x
      }
    }

    const target = {
      startX: item.x,
      startY: item.y,
      endX: radius,
      endY: radius,
      fillStyle: '#fff',
      strokeStyle: '#fff',
      type
    }

    if (!item.isEmpty) {
      info.rects[idx] = target
    }

    const triangleInfo = {
      x: item.x + halfRadius,
      y: item.y + (idx < info.divideCount ? radius : 0),
      fillStyle: '#fff',
      direct: idx < info.divideCount ? 'down' : 'up'
    }
    if (!item.isEmpty) {
      info.triangles[idx] = triangleInfo
    }

    const tagLen = getPartLen(idx + 1)
    const tagTexts = {
      startX: item.x + (0.4 * radius - radius / 4) / 2 + (tagLen > 1 ? -1 : 1),
      startY: item.y - 0.2 * radius,
      text: `${idx + 1}`,
      fillStyle: '#fff',
      fontSize: radius / 4
    }
    if (!item.isEmpty) {
      info.tagTexts[idx] = tagTexts
    }

    const tagInfo = {
      startX: item.x,
      startY: item.y,
      endX: 0.4 * radius,
      endY: 0.4 * radius,
      fillStyle: '#1890ff',
      strokeStyle: '#1890ff'
    }
    if (!item.isEmpty) {
      info.tags[idx] = tagInfo
    }
    return item
  })

  info.data = newData
}

const reDraw = () => {
  clear()
  cancelAnimationFrame(info.raf)
  init()

  if (info.data.length) {
    drawInitRects()
    animate()
  }
}

onMounted(() => {
  info.data =
    props.data.length % 2 === 0
      ? props.data
      : [...props.data, { parts: [], color: 'transparent', isEmpty: true }]

  info.maxCircle = info.data.reduce((prev, item) => {
    const children = item.proOfCircleVOList
    const childrenCircles = children.reduce((prev, item) => {
      prev.push(item.circle)
      return prev
    }, [])
    const max = Math.max(...childrenCircles)
    // max
    return Math.max(prev, ...item.circle)
  }, 0)
  const canvas = document.getElementById(props.id)
  info.ctx = canvas.getContext('2d')

  info.ctx.fillRect(0, 0, info.width, info.height)

  reDraw()
  drawInitBackgroundLine()
})
</script>

<template>
  <canvas
    class="canvas"
    :id="props.id"
    :width="info.width"
    :height="info.height"
  ></canvas>
</template>

<style>
.config {
  display: flex;
  flex-direction: column;
}

.canvas {
  filter: drop-shadow(4px 4px 8px rgba(0, 0, 0, 0.2));
}
</style>
